/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.util;

import bsh.NameSpace;
import bsh.UtilEvalError;
import cn.easyplatform.*;
import cn.easyplatform.castor.Castors;
import cn.easyplatform.contexts.Contexts;
import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dao.BizDao;
import cn.easyplatform.dos.*;
import cn.easyplatform.entities.beans.LogicBean;
import cn.easyplatform.entities.beans.bpm.BpmBean;
import cn.easyplatform.entities.beans.list.ListBean;
import cn.easyplatform.entities.beans.page.BindVariable;
import cn.easyplatform.entities.beans.page.PageBean;
import cn.easyplatform.entities.beans.project.ProjectBean;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.table.TableField;
import cn.easyplatform.entities.beans.task.DecisionBean;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.entities.beans.task.TransitionBean;
import cn.easyplatform.entities.beans.task.Variable;
import cn.easyplatform.entities.helper.EventLogic;
import cn.easyplatform.i18n.I18N;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Mirror;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.ListInitVo;
import cn.easyplatform.messages.vos.ListVo;
import cn.easyplatform.messages.vos.PageVo;
import cn.easyplatform.messages.vos.TaskRuntimeVo;
import cn.easyplatform.services.IProjectService;
import cn.easyplatform.support.scripting.*;
import cn.easyplatform.support.sql.SqlFieldParser;
import cn.easyplatform.support.sql.SqlParser;
import cn.easyplatform.support.sql.SqlPrimitiveParser;
import cn.easyplatform.support.word.ReserveWordFactory;
import cn.easyplatform.type.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Wrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public final class RuntimeUtils {

    private final static Logger log = LoggerFactory.getLogger(RuntimeUtils.class);

    // ///////////////////////////系统变量//////////////////////////////////

    /**
     * @param params
     * @param tb
     */
    public static void initWorkflow(Map<String, Object> params, TaskBean tb) {
        CommandContext cc = Contexts.getCommandContext();
        IProjectService ps = cc.getProjectService();
        ProjectBean pb = ps.getEntity();
        // 所属的项目信息,从701至719
        EnvDo env = cc.getEnv();
        //params.put("700", cc.getEngineConfiguration().getWorkPath());
        params.put("701", cc.getEngineConfiguration().getAppPath());
        params.put("702", pb.getId());
        params.put("703", pb.getName());
        params.put("704", pb.getAppContext());
        params.put("705", pb.getBizDb());
        params.put("706", pb.getIdentityDb());
        params.put("707", ps.getWorkspace());
        params.put("708", ps.getWorkspace());
        params.put("709", env.getLocale());
        params.put("710", env.getPortlet());
        params.put("711", ps.getEntity().getEntityTableName());

        // 所属的用户信息,从720到739
        UserDo user = cc.getUser();
        if (user != null)
            initUser(params, user);

        if (tb != null) {
            // 主功能信息800-809
            params.put("801", tb.getId());
            params.put("802", tb.getName());
            params.put("803", tb.getDescription());
            params.put("804", tb.getExecuteType() == null ? "0" : tb.getExecuteType().toString());
        }
    }

    public static void initUser(Map<String, Object> params, UserDo user) {
        // 所属的用户信息,从720到739
        params.put("720", user.getId());
        params.put("721", user.getName());
        params.put("722", user.getOrg() == null ? "" : user.getOrg()
                .getId());
        params.put("723", user.getOrg() == null ? "" : user.getOrg()
                .getName());
        params.put("724", user.getRoles());
        params.put("725", user.getIp());
        params.put("726", user.getLocale());
        params.put("727", user.getLoginDate());
        params.put("728", user.getDeviceType().getName());
        params.put("729", user.getWorkspace());
        params.put("730", user.getWorkspace());
        params.put("731", user.getType());

        if (user.getOrg() != null)
            params.putAll(user.getOrg().getExtraInfo());

        // 其它自定义系统变量从760开始
        if (user.getExtraInfo() != null)
            params.putAll(user.getExtraInfo());
    }

    /**
     * @param params
     * @param tb
     */
    public static void initTask(Map<String, Object> params, TaskBean tb) {
        // 功能变量810-829
        params.put("810", tb.getId());
        params.put("811", tb.getName());
        params.put("812", tb.getDescription());
        params.put("813", tb.getImage());
        params.put("814", tb.getProcessCode());
        params.put("815", tb.isUpdatable());
        params.put("816", tb.isVisible());
        params.put("817", tb.getDecision());
        // 事件
        params.put("818", tb.getOnInit());
        params.put("819", tb.getOnBeforeCommit());
        params.put("820", tb.getOnAfterCommit());
        params.put("821", tb.getOnRollback());
        params.put("822", tb.getOnCommitted());
        params.put("823", tb.getOnCommittedRollback());
        params.put("824", tb.getOnPreCommit());
        params.put("825", tb.getOnCommittedRollback());
        params.put("826", tb.getOnClose());
        params.put("827", tb.getInheritId());
    }

    /**
     * @param cc
     * @param pb
     */
    public static void initPage(CommandContext cc, WorkflowContext ctx,
                                PageBean pb) {
        // 页面信息830-839
        ctx.setParameter("830", pb.getType());
        ctx.setParameter("831", pb.getId()).setParameter("832", pb.getName());
        ctx.setParameter("833", pb.getTable());

        // 事件
        ctx.setParameter("835", pb.getOnLoad());
        ctx.setParameter("836", pb.getOnRefresh());
        ctx.setParameter("837", pb.getOnOk());
        ctx.setParameter("838", pb.getOnBack());
        ctx.setParameter("839", pb.getInheritId());
    }

    public static void initBpm(WorkflowContext ctx, BpmBean bb) {
        ctx.setParameter("860", bb.getId());
        ctx.setParameter("861", bb.getName());
        ctx.setParameter("862", bb.getDescription());
    }

    public static void initList(Map<String, Object> params, ListInitVo lv,
                                ListBean bean) {
        /*
         * LIST的变量有： LIST_ID:页面上组件的id LIST_TYPE:类型850-859
         */
        params.put("850", lv.getId());
        params.put("851", bean.getId());
        params.put("852", lv.getType());
        params.put("853", Boolean.FALSE);
        params.put("833", bean.getTable());
        // 清除主功能的逻辑
        for (int i = 818; i < 827; i++)
            params.remove(String.valueOf(i));
    }

    /**
     * @param vars
     * @param tb
     */
    public static void initTaskVars(Map<String, FieldDo> vars, TaskBean tb) {
        if (tb.getVariables() != null) {
            for (Variable var : tb.getVariables()) {
                FieldDo fd = new FieldDo(var.getType());
                fd.setDecimal(var.getDecimal());
                fd.setLength(var.getLength());
                fd.setScope(var.getScope());
                fd.setName(var.getName());
                fd.setDescription(var.getDesp());
                fd.setShareModel(var.getShareModel());
                // 变量初始化
                if (!Strings.isBlank(var.getValue()))
                    fd.setValue(castTo(var.getValue(), fd));
                vars.put(fd.getName(), fd);
            }
        }
    }

    // //////////////////////表操作函数////////////////////////////

    /**
     * 客户端列表数据转换成为后端可计算的记录
     *
     * @param data
     * @return
     */
    public static Record createRecord(Object[] data) {
        Record record = new Record();
        for (Object obj : data)
            record.set(castTo((FieldVo) obj));
        return record;
    }

    /**
     * @param table
     * @return
     */
    public static Record createRecord(CommandContext cc, TableBean table,
                                      boolean isGenKey) {
        Record record = new Record();
        for (TableField tf : table.getFields()) {
            FieldDo fd = new FieldDo(tf.getType());
            fd.setName(tf.getName());
            fd.setAcc(tf.getAcc());
            fd.setLength(tf.getLength());
            fd.setDecimal(tf.getDecimal());
            fd.setDescription(tf.getDescription());
            // 栏位值初始化
            if (!Strings.isBlank(tf.getDefaultValue()))
                fd.setValue(castTo(tf.getDefaultValue(), fd));
            fd.setOptionType(tf.getOptionType() == null ? 0 : tf.getOptionType());
            fd.setOptionValue(tf.getOptionValue());
            record.set(fd);
        }
        if (isGenKey && table.getKey() != null && !table.getKey().isEmpty()) {
            FieldDo fd = record.get(table.getKey().get(0));
            if (fd.getType() != FieldType.LONG && fd.getType() != FieldType.INT
                    && fd.getType() != FieldType.NUMERIC)
                throw new EasyPlatformWithLabelKeyException("table.key.invalid", table.getId(), table.getKey());
            fd.setValue(cc.getIdGenerator().getNextId(table.getId()));
//            if (table.isByDb()) {
//                //删除自增加栏位，使用表自增长
//                //record.getColumnNames().remove(fd.getName());
//            } else {
//                fd.setValue(cc.getIdGenerator().getNextId(table.getId()));
//            }
        }
        record.setKey(table.getKey());
        return record;
    }

    private static FieldDo createField(TableField field, Object v) {
        FieldDo fd = new FieldDo(field.getType());
        fd.setName(field.getName());
        fd.setLength(field.getLength());
        fd.setDecimal(field.getDecimal());
        fd.setDescription(field.getDescription());
        fd.setValue(v);
        fd.setOptionType(field.getOptionType() == null ? 0 : field.getOptionType());
        fd.setOptionValue(field.getOptionValue());
        return fd;
    }

    public static FieldDo createPrimaryKey(TableBean table, long keyValue) {
        if (table.getKey() != null && table.getKey().size() == 1) {
            String keyField = table.getKey().get(0);
            for (TableField field : table.getFields()) {
                if (field.getName().equals(keyField))
                    return createField(field, keyValue);
            }
            throw new FieldNotFoundException(
                    "entity.table.field.not.found", table.getId(), keyField);
        }
        throw new EasyPlatformWithLabelKeyException(
                "datalist.field.key.not.match", table.getId());
    }

    public static List<FieldDo> createPrimaryKey(TableBean tb, Record record) {
        List<FieldDo> pk = new ArrayList<FieldDo>(tb.getKey().size());
        for (String name : tb.getKey()) {
            FieldDo field = record.get(name);
            if (field == null)
                throw new FieldNotFoundException(
                        "entity.table.field.not.found", tb.getId(), name);
            pk.add(field);
        }
        return pk;
    }

    public static List<FieldDo> createPrimaryKey(TableBean tb,
                                                 Object[] keyValues, StringBuilder sb) {
        if (tb.getKey().size() != keyValues.length)
            throw new EasyPlatformWithLabelKeyException(
                    "datalist.field.key.not.match", tb.getId());
        List<FieldDo> pk = new ArrayList<FieldDo>(keyValues.length);
        for (int i = 0; i < keyValues.length; i++) {
            String key = tb.getKey().get(i);
            for (TableField field : tb.getFields()) {
                if (field.getName().equals(key)) {
                    pk.add(createField(field, keyValues[i]));
                    if (sb != null) {
                        sb.append(field.getName()).append("=?");
                        if (i < (keyValues.length - 1))
                            sb.append(" and ");
                    }
                    break;
                }
            }
        }
        return pk;
    }

    public static Record getTableRecord(CommandContext cc, TableBean tb,
                                        RecordContext rc) {
        BizDao dao = cc.getBizDao(tb.getSubType());
        List<FieldDo> params = new ArrayList<FieldDo>();
        for (String keyField : tb.getKey()) {
            for (TableField field : tb.getFields()) {
                if (field.getName().equals(keyField)) {
                    params.add(createField(field, rc.getValue(keyField)));
                    break;
                }
            }
        }
        Record record = dao.selectByKey(tb, params);
        if (record == null)
            throw new EasyPlatformWithLabelKeyException(
                    "table.record.not.found", tb.getId(), Lang.concat(
                    params.toArray()).toString());
        return record;
    }

    public static Record getTableRecord(CommandContext cc, TableBean tb,
                                        Object[] keys) {
        BizDao dao = cc.getBizDao(tb.getSubType());
        List<FieldDo> params = new ArrayList<FieldDo>();
        int index = 0;
        for (String keyField : tb.getKey()) {
            for (TableField field : tb.getFields()) {
                if (field.getName().equals(keyField)) {
                    params.add(createField(field, keys[index++]));
                    break;
                }
            }
        }
        Record record = dao.selectByKey(tb, params);
        if (record == null)
            throw new EasyPlatformWithLabelKeyException(
                    "table.record.not.found", tb.getId(), Lang.concat(
                    params.toArray()).toString());
        return record;
    }

    /**
     * @return
     */
    public static <T> SqlParser<T> createSqlParser(Class<T> clazz) {
        if (clazz == FieldDo.class)
            return new SqlFieldParser();
        else
            return new SqlPrimitiveParser();
    }

    /**
     * 解析逻辑
     *
     * @param cc
     * @param el
     * @return
     */
    private static String parse(CommandContext cc, RecordContext rc, EventLogic el) {
        String exp = el.getExpression();
        if (Strings.isBlank(exp)) {
            if (el.getId() != null) {
                LogicBean lb = cc.getEntity(el.getId());
                if (lb == null)
                    throw new EntityNotFoundException(
                            EntityType.LOGIC.getName(), el.getId());
                exp = lb.getContent();
                if (log.isDebugEnabled())
                    log.debug("logic->{},{}", lb.getId(), lb.getName());
            } else if (!Strings.isBlank(el.getContent())) {
                exp = el.getContent();
                if (log.isDebugEnabled())
                    log.debug("expression->{}", exp);
            }
            if (exp == null)
                return null;
            exp = ScriptUtils.parse(cc, rc, exp.trim());
            el.setExpression(exp);
        }
        return exp;
    }

    /**
     * @param rc
     * @param src
     * @return
     */
    public final static EventLogic castTo(CommandContext cc, RecordContext rc,
                                          Object src) {
        if (src == null)
            return null;
        EventLogic el = null;
        if (src instanceof EventLogic) {
            el = (EventLogic) src;
            if (cc.getUser().isDebugEnabled())
                cc.send(I18N.getLabel("script.engine.run", el.getId()));
        } else if (src instanceof LogicBean) {
            el = new EventLogic();
            LogicBean lb = (LogicBean) src;
            el.setId(lb.getId());
            el.setContent(lb.getContent().trim());
        } else {
            String expr = src.toString().trim();
            if (expr.startsWith("$")) {
                char[] cs = expr.toCharArray();
                int i = 1;
                boolean isVar = true;
                while (i < cs.length) {
                    if (!Character.isLetterOrDigit(cs[i])) {
                        isVar = false;
                        break;
                    }
                    i++;
                }
                cs = null;
                if (isVar) {
                    expr = (String) rc.getValue(expr.substring(1));
                    if (expr != null)
                        expr = expr.trim();
                }
            }
            if (Strings.isBlank(expr))
                return null;
            el = new EventLogic();
            if (expr.indexOf("(") > 0 || expr.indexOf(";") > 0
                    || expr.indexOf("=") > 0)
                el.setContent(src.toString().trim());
            else {
                LogicBean lb = cc.getEntity(expr);
                if (lb == null)
                    throw new EntityNotFoundException(
                            EntityType.LOGIC.getName(), expr);
                if (cc.getUser().isDebugEnabled())
                    cc.send(I18N.getLabel("script.engine.run", lb.getId()));
                el.setId(lb.getId());
                el.setContent(lb.getContent().trim());
            }
        }
        return el;
    }

    /**
     * @param cc
     * @param script
     * @param target
     * @param source
     * @return
     */
    public final static String eval(CommandContext cc, Object script,
                                    RecordContext target, FieldDo[] source) {
        Record record = new Record();
        for (FieldDo fd : source)
            record.set(fd);
        RecordContext rc = target.clone();
        rc.setData(record);
        return eval(cc, script, rc, target);
    }

    /**
     * 计算表达式
     *
     * @param cc
     * @param expression
     * @param scope
     * @return
     */
    public final static Object eval(CommandContext cc, String expression, RecordContext rc, RhinoScriptable scope) {
        try {
            EventLogic el = castTo(cc, rc, expression);
            if (el == null)
                return null;
            expression = ScriptUtils.parse(cc, rc, el.getContent());
            Context cx = Context.enter();
            cx.setOptimizationLevel(-1);
            Object result = cx.evaluateString(scope, expression, "", 1, null);
            if (result == null)
                return null;
            else if (result instanceof Wrapper)
                return ((Wrapper) result).unwrap();
            else
                return result;
        } catch (RhinoException ex) {
            throw ScriptUtils.handleScriptException(cc, ex);
        } finally {
            Context.exit();
        }
    }

    /**
     * @param cc
     * @param src
     * @param rcs
     * @return
     */
    public final static String eval(CommandContext cc, Object src,
                                    RecordContext... rcs) {
        EventLogic el = castTo(cc, rcs[0], src);
        if (el == null)
            return "0000";
        RecordContext rc = rcs.length > 1 ? rcs[1] : rcs[0];
        String exp = parse(cc, rc, el);
        if (exp != null) {
            ScriptEngine compiler = null;
            try {
                compiler = ScriptEngineFactory.createEngine().init(
                        new ScriptEntry(cc, exp, rcs));
                compiler.eval(1);
                closeQuietly(compiler);
            } catch (ScriptEvalExitException ex) {
                String result = ex.getMessage().toLowerCase();
                if (result.length() != 4) {// 直接提示信息,方便调试变量
                    rc.setParameter("755", ex.getMessage());
                    return "i000";
                }
                if (result.charAt(0) == 'c' || result.charAt(0) == 'w') {
                    // 在这里需要记住运行时的位置,设置断点
                    cc.setBreakPoint(new BreakPoint(el,
                            compiler.getNameSpace(), ex.getLine()));
                    return result;
                } else {
                    closeQuietly(compiler);
                    // 否则直接返回错误代码
                    return ex.getMessage();
                }
            } catch (ScriptEvalException ex) {
                closeQuietly(compiler);
                ex.setSource(el.toString());
                throw ex;
            } catch (EasyPlatformWithLabelKeyException ex) {
                closeQuietly(compiler);
                ex.setSource(el.toString());
                throw ex;
            } catch (Exception ex) {
                closeQuietly(compiler);
                throw Lang.wrapThrow(ex);
            }
        }
        return "0000";
    }

    /**
     * 断点执行
     *
     * @param cc
     * @param bp
     * @return
     */
    public static String eval(CommandContext cc, BreakPoint bp) {
        ScriptEngine compiler = null;
        try {
            compiler = ScriptEngineFactory.createEngine().init(
                    new ScriptEntry(cc, bp.getEventLogic().getExpression(), bp
                            .getNameSpace()));
            compiler.eval(bp.getLine() + 1);
            cc.setBreakPoint(null);
            closeQuietly(compiler);
        } catch (ScriptEvalExitException ex) {
            String result = ex.getMessage().toLowerCase();
            if (result.charAt(0) == 'c' || result.charAt(0) == 'w') {
                // 在这里需要记住运行时的位置,设置断点
                cc.setBreakPoint(new BreakPoint(bp.getEventLogic(), compiler
                        .getNameSpace(), ex.getLine()));
                return result;
            } else {
                closeQuietly(compiler);
                cc.setBreakPoint(null);
                // 否则直接返回错误代码
                return ex.getMessage();
            }
        } catch (ScriptEvalException ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            ex.setSource(bp.getEventLogic().toString());
            throw ex;
        } catch (EasyPlatformWithLabelKeyException ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            ex.setSource(bp.getEventLogic().toString());
            throw ex;
        } catch (Exception ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            throw Lang.wrapThrow(ex);
        }
        return "0000";
    }

    /**
     * @param cc
     * @param src
     * @param rc
     * @return
     */
    public final static Object evaluate(CommandContext cc, Object src,
                                        RecordContext rc) {
        EventLogic el = castTo(cc, rc, src);
        if (el == null)
            return null;
        String exp = parse(cc, rc, el);
        if (exp != null) {
            ScriptEngine compiler = null;
            try {
                compiler = ScriptEngineFactory.createEngine().init(
                        new ScriptEntry(cc, exp, new RecordContext[]{rc}));
                Object val = compiler.eval(1);
                closeQuietly(compiler);
                return val;
            } catch (ScriptEvalExitException ex) {
                String result = ex.getMessage().toLowerCase();
                if (result.length() != 4) {// 直接提示信息,方便调试变量
                    rc.setParameter("755", ex.getMessage());
                    return "i000";
                }
                if (result.charAt(0) == 'c' || result.charAt(0) == 'w') {
                    // 在这里需要记住运行时的位置,设置断点
                    cc.setBreakPoint(new BreakPoint(el,
                            compiler.getNameSpace(), ex.getLine()));
                    return result;
                } else {
                    closeQuietly(compiler);
                    // 否则直接返回错误代码
                    return ex.getMessage();
                }
            } catch (ScriptEvalException ex) {
                closeQuietly(compiler);
                ex.setSource(el.toString());
                throw ex;
            } catch (EasyPlatformWithLabelKeyException ex) {
                closeQuietly(compiler);
                ex.setSource(el.toString());
                throw ex;
            } catch (Exception ex) {
                closeQuietly(compiler);
                throw Lang.wrapThrow(ex);
            }
        }
        return null;
    }

    /**
     * 断点执行
     *
     * @param cc
     * @param bp
     * @return
     */
    public static Object evaluate(CommandContext cc, BreakPoint bp) {
        ScriptEngine compiler = null;
        try {
            compiler = ScriptEngineFactory.createEngine().init(
                    new ScriptEntry(cc, bp.getEventLogic().getExpression(), bp
                            .getNameSpace()));
            Object val = compiler.eval(bp.getLine() + 1);
            cc.setBreakPoint(null);
            closeQuietly(compiler);
            return val;
        } catch (ScriptEvalExitException ex) {
            String result = ex.getMessage().toLowerCase();
            if (result.charAt(0) == 'c' || result.charAt(0) == 'w') {
                // 在这里需要记住运行时的位置,设置断点
                cc.setBreakPoint(new BreakPoint(bp.getEventLogic(), compiler
                        .getNameSpace(), ex.getLine()));
                return result;
            } else {
                closeQuietly(compiler);
                cc.setBreakPoint(null);
                // 否则直接返回错误代码
                return ex.getMessage();
            }
        } catch (ScriptEvalException ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            ex.setSource(bp.getEventLogic().toString());
            throw ex;
        } catch (EasyPlatformWithLabelKeyException ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            ex.setSource(bp.getEventLogic().toString());
            throw ex;
        } catch (Exception ex) {
            closeQuietly(compiler);
            cc.setBreakPoint(null);
            throw Lang.wrapThrow(ex);
        }
    }

    /**
     * @param cc
     * @param rcs
     * @param expr
     * @return
     */
    public final static Object evalExpr(CommandContext cc, String expr,
                                        RecordContext... rcs) {
        EventLogic el = castTo(cc, rcs.length > 1 ? rcs[1] : rcs[0], expr);
        try {
            Object obj = ScriptEngineFactory.createExpressionEngine().eval(cc,
                    el.getContent(), rcs);
            return obj;
        } catch (ScriptEvalException ex) {
            cc.setBreakPoint(null);
            ex.setSource(el.toString());
            throw ex;
        } catch (EasyPlatformWithLabelKeyException ex) {
            cc.setBreakPoint(null);
            ex.setSource(el.toString());
            throw ex;
        } catch (Exception ex) {
            cc.setBreakPoint(null);
            throw Lang.wrapThrow(ex);
        }
    }

    /**
     * @param engine
     */
    private static void closeQuietly(ScriptEngine engine) {
        if (engine != null) {
            engine.destroy();
            engine = null;
        }
    }

    // /////////////////辅助函数//////////////////////////////

    public final static FieldVo castTo(FieldDo fd) {
        return castTo(fd, null);
    }

    public final static FieldDo castTo(FieldVo fv) {
        FieldDo fd = new FieldDo(fv.getType(), fv.getValue());
        fd.setDecimal(fv.getDecimal());
        fd.setDescription(fv.getDescription());
        fd.setLength(fv.getLength());
        fd.setName(fv.getName());
        return fd;
    }

    public final static FieldVo castTo(FieldDo fd, String name) {
        FieldVo fv = new FieldVo(fd.getType());
        fv.setDecimal(fd.getDecimal());
        fv.setLength(fd.getLength());
        fv.setAcc(fd.getAcc());
        if (name == null)
            fv.setName(fd.getName());
        else
            fv.setName(name);
        if (fd.getValue() != null) {
            if (fd.getValue() instanceof Serializable)
                fv.setValue(fd.getValue());
            else
                fv.setValue(fd.getValue().toString());
        }
        fv.setDescription(fd.getDescription());
        fv.setOptionType(Option.Type.valueOf(fd.getOptionType()));
        fv.setOptionValue(fd.getOptionValue());
        return fv;
    }

    public final static Object castTo(FieldDo fd, Object value) {
        if (value == null)
            return null;
        switch (fd.getType()) {
            case CHAR:
                if (value.toString().equals(""))
                    return ' ';
                return value.toString().charAt(0);
            case VARCHAR:
            case CLOB:
                return value.toString();
            case BOOLEAN:
                if (value instanceof Boolean)
                    return (Boolean) value;
                if (value instanceof Number)
                    return ((Number) value).intValue() > 0;
                return value.toString().equalsIgnoreCase("true");
            case DATETIME:
            case TIME:
            case DATE:
                if (value instanceof Date)
                    return value;
                if (value instanceof Calendar)
                    return ((Calendar) value).getTime();
                return value;
            case INT:
                if (value instanceof Number)
                    return ((Number) value).intValue();
                return Nums.toInt(value, 0);
            case LONG:
                if (value instanceof Number)
                    return ((Number) value).longValue();
                return Nums.toLong(value, 0);
            case NUMERIC:
                if (value instanceof Number)
                    return ((Number) value).doubleValue();
                return Double.parseDouble(value.toString());
            case OBJECT:
            case BLOB:
                return value;
            default:
                throw new EasyPlatformWithLabelKeyException("dao.access.getType",
                        fd.getName(), fd.getType());
        }
    }

    public final static Object castTo(String str, FieldDo tf) {
        if (str.startsWith("&"))
            return ReserveWordFactory.getReserveWord(str.substring(1));
        switch (tf.getType()) {
            case CHAR:
                if (str.length() > 0)
                    return str.charAt(0);
                return ' ';
            case VARCHAR:
            case CLOB:
            case OBJECT:
            case BLOB:
                return str;
            case BOOLEAN:
                return Castors.me().castTo(str, boolean.class);
            case DATETIME:
            case TIME:
            case DATE:
                return Castors.me().castTo(str, Date.class);
            case INT:
                return Castors.me().castTo(str, int.class);
            case LONG:
                return Castors.me().castTo(str, long.class);
            case NUMERIC:
                return Castors.me().castTo(str, double.class);
            default:
                throw new EasyPlatformWithLabelKeyException("dao.access.getType",
                        tf.getName(), tf.getType());
        }
    }

    public final static Object castTo(Object val) {
        if (val instanceof Wrapper)
            return ((Wrapper) val).unwrap();
        else if (val instanceof IdScriptableObject) {
            IdScriptableObject obj = (IdScriptableObject) val;
            if (obj.getClassName().equals("Date"))
                return Context.jsToJava(obj, Date.class);
            else if (obj.getClassName().equals("Array"))
                return Context.jsToJava(obj, Object[].class);
            else if (obj.getClassName().equals("Boolean"))
                return Context.jsToJava(obj, Boolean.class);
            else if (obj.getClassName().equals("String"))
                return Context.jsToJava(obj, String.class);
            else if (obj.getClassName().equals("Number"))
                return Context.jsToJava(obj, Number.class);
            else if (obj.getClassName().equals("Object"))
                return Context.jsToJava(obj, Map.class);
            else
                return obj;
        } else if (val instanceof JSONArray) {
            JSONArray fj = (JSONArray) val;
            List<Object> result = new ArrayList<>(fj.size());
            for (Object o : fj) {
                if (o instanceof JSONObject) {
                    result.add(((JSONObject) o).getInnerMap());
                } else {
                    result.add(o);
                }
            }
            return result;
        } else if (val instanceof JSONObject)
            return ((JSONObject) val).getInnerMap();
        return val;
    }

    /**
     * @param v
     * @return
     */
    public static final FieldDo castTo(String name, Object v) {
        if (v instanceof FieldDo)
            return (FieldDo) v;
        return new FieldDo(name, FieldType.cast(v), v);
    }

    public final static void log(CommandContext cc, int type, String event,
                                 String content) {
        UserDo user = cc.getUser();
        BizDao dao = cc.getBizDao();
        dao.log(new LogDo(user.getEventId(), user.getId(), user.getDeviceType()
                .getName(), type, event, content));
    }

    public final static String getLabel(CommandContext cc, String code) {
        if (code == null)
            return null;
        if (code.startsWith("#{'") && code.endsWith("'}")) {
            code = StringUtils.substringBetween(code, "#{'", "'}");
            return cc.getLabel(code);
        } else
            return code;
    }

    public final static String getLabel(CommandContext cc, String code, String locale) {
        if (code == null)
            return null;
        if (code.startsWith("#{'") && code.endsWith("'}")) {
            code = StringUtils.substringBetween(code, "#{'", "'}");
            return cc.getLabel(code, locale);
        } else
            return code;
    }

    public final static String getLabel(CommandContext cc, RecordContext rc,
                                        String code) {
        if (code == null)
            return null;
        if (code.startsWith("#{'") && code.endsWith("'}")) {
            code = StringUtils.substringBetween(code, "#{'", "'}");
            if (code.startsWith("$"))
                code = (String) rc.getValue(code.substring(1));
            return cc.getLabel(code);
        } else if (code.startsWith("$"))
            return (String) rc.getValue(code.substring(1));
        return code;
    }

    /**
     * 动态设置对象值
     *
     * @param instance
     * @param value0
     * @param value1
     * @param interClass0
     * @param interClass1
     * @return
     */
    public final static boolean injectObject(Object instance, Object value0, Object value1, Class<?> interClass0, Class<?> interClass1) {
        Mirror<?> me = Mirror.me(instance);
        Field[] fields = me.getFields();
        if (fields.length == 1 && fields[0].getName().startsWith("_bsh")) {//由bsh编译的class
            Object val = me.getValue(instance, fields[0]);
            bsh.NameSpace ns = null;
            if (val instanceof bsh.NameSpace) {
                ns = (NameSpace) val;
            } else if (val instanceof bsh.This) {
                ns = ((bsh.This) val).getNameSpace();
            }
            if (ns == null)
                return false;
            try {
                for (bsh.Variable variable : ns.getDeclaredVariables()) {
                    if (variable.getType().isAssignableFrom(interClass0)) {
                        variable.setValue(value0, 0);
                    } else if (variable.getType().isAssignableFrom(interClass1)) {
                        variable.setValue(value1, 0);
                    }
                }
            } catch (UtilEvalError utilEvalError) {
                if (log.isErrorEnabled())
                    log.error("variable.setValue", utilEvalError);
                return false;
            }
        } else {//外部jar或class
            for (Field field : fields) {
                if (field.getType().isAssignableFrom(interClass0)) {
                    me.setValue(instance, field, value0);
                } else if (field.getType().isAssignableFrom(interClass1)) {
                    me.setValue(instance, field, value1);
                }
            }
        }
        return true;
    }

    /**
     * 动态设置对象值
     *
     * @param instance
     * @param value
     * @param interClass
     * @return
     */
    public final static boolean injectObject(Object instance, Object value, Class<?> interClass) {
        Mirror<?> me = Mirror.me(instance);
        Field[] fields = me.getFields();
        if (fields.length == 1 && fields[0].getName().startsWith("_bsh")) {
            Object val = me.getValue(instance, fields[0]);
            bsh.NameSpace ns = null;
            if (val instanceof bsh.NameSpace) {
                ns = (NameSpace) val;
            } else if (val instanceof bsh.This) {
                ns = ((bsh.This) val).getNameSpace();
            }
            if (ns == null)
                return false;
            for (bsh.Variable variable : ns.getDeclaredVariables()) {
                if (variable.getType().isAssignableFrom(interClass)) {
                    try {
                        variable.setValue(value, 0);
                    } catch (UtilEvalError utilEvalError) {
                        if (log.isErrorEnabled())
                            log.error("variable.setValue", utilEvalError);
                        return false;
                    }
                    break;
                }
            }
        } else {
            for (Field field : fields) {
                if (field.getType().isAssignableFrom(interClass)) {
                    me.setValue(instance, field, value);
                    break;
                }
            }
        }
        return true;
    }

    /**
     * 页面绑定变量
     *
     * @param cc
     * @param ctx
     * @param variables
     * @param pv
     */
    public final static void bindVariables(CommandContext cc, WorkflowContext ctx, List<BindVariable> variables, PageVo pv) {
        pv.setBindVariables(new HashMap<>());
        for (BindVariable wv : variables) {
            if (!Strings.isBlank(wv.getName()) && !Strings.isBlank(wv.getRef())) {
                String ref = wv.getRef().trim();
                if (ref.startsWith("$"))
                    wv.setRef((String) ctx.getRecord().getValue(ref.substring(1)));
                if (!Strings.isBlank(ref)) {
                    if (ref.toLowerCase().startsWith("select ")) {
                        BizDao dao = cc.getBizDao(wv.getDsId());
                        SqlParser<Object> parser = RuntimeUtils.createSqlParser(Object.class);
                        pv.getBindVariables().put(wv.getName(), dao.selectMapList(parser.parse(ref, ctx.getRecord()), null, parser.getParams().toArray()));
                    } else {
                        Object value = ctx.getRecord().getValue(ref);
                        if (value != null && !(value instanceof Serializable))
                            throw new EasyPlatformWithLabelKeyException("page.bind.value.error");
                        pv.getBindVariables().put(wv.getName(), value);
                    }
                }//if
            }//if
        }//if
    }

    /**
     * 获取功能运行信息
     *
     * @param ctx
     * @return
     */
    public final static TaskRuntimeVo createRuntime(WorkflowContext ctx, boolean detail) {
        TaskRuntimeVo trv = new TaskRuntimeVo();
        RecordContext rc = ctx.getRecord();
        for (Map.Entry<String, Object> entry : rc.getSystemVars()) {
            if (!detail && entry.getKey().startsWith("7"))
                continue;
            if (entry.getValue() != null) {
                if (entry.getValue() instanceof String[])
                    trv.setSystemVar(entry.getKey(),
                            Lang.concat(",", (String[]) entry.getValue()));
                else if (entry.getValue() instanceof EventLogic) {
                    EventLogic el = (EventLogic) entry.getValue();
                    if (Strings.isBlank(el.getId()))
                        trv.setSystemVar(entry.getKey(), el.getContent());
                    else
                        trv.setSystemVar(entry.getKey(), el.getId());
                } else if (entry.getValue() instanceof DecisionBean) {
                    DecisionBean db = (DecisionBean) entry.getValue();
                    StringBuilder sb = new StringBuilder();
                    sb.append("<decision expr=\"").append(db.getExpr())
                            .append("\">\n");
                    for (TransitionBean tsb : db.getTransitions()) {
                        sb.append("<transition name=\"").append(tsb.getName())
                                .append("\" displayName=\"")
                                .append(tsb.getDisplayName())
                                .append("\" to=\"").append(tsb.getTo())
                                .append("\"");
                        if (!Strings.isBlank(tsb.getFrom()))
                            sb.append(" from=\"").append(tsb.getFrom())
                                    .append("\"");
                        if (!Strings.isBlank(tsb.getLogicId()))
                            sb.append(" logicId=\"").append(tsb.getLogicId())
                                    .append("\"");
                        sb.append(">\n");
                        if (!Strings.isBlank(tsb.getMappings()))
                            sb.append(tsb.getMappings());
                        sb.append("</transition>\n");
                    }
                    sb.append("</decision>");
                    trv.setSystemVar(entry.getKey(), sb.toString());
                } else
                    trv.setSystemVar(entry.getKey(), entry.getValue());
            } else
                trv.setSystemVar(entry.getKey(), null);
        }
        for (FieldDo fd : rc.getUserVars())
            trv.setUserVar(RuntimeUtils.castTo(fd));
        if (rc.getData() != null) {
            for (FieldDo fd : rc.getData().getData())
                trv.setFieldVar(RuntimeUtils.castTo(fd));
        }
        for (ListContext lc : ctx.getLists()) {
            ListVo lv = new ListVo();
            lv.setEntity(lc.getBean().getId());
            lv.setType(lc.getType());
            lv.setId(lc.getId());
            lv.setName(lc.getBean().getName());
            if (detail) {
                lv.setSql(lc.getSql());
                lv.setOrderBy(lc.getOrderBy());
                lv.setTableId(lc.getBean().getTable());
                if (lc.getParams() != null && !lc.getParams().isEmpty()) {
                    StringBuilder sb = new StringBuilder();
                    for (FieldDo fd : lc.getParams()) {
                        sb.append("[").append(fd.getName()).append(":")
                                .append(fd.getValue()).append("]");
                    }
                    lv.setParams(sb.toString());
                    sb = null;
                }
            }
            trv.setListVar(lv);
        }
        return trv;
    }

    /**
     * 创建游客用户
     *
     * @param env
     * @param ps
     * @return
     */
    public final static UserDo createGustUser(String id, int type, EnvDo env, IProjectService ps) {
        UserDo guest = new UserDo();
        guest.setId(id);
        guest.setName(I18N.getLabel("user." + id));
        guest.setType(type);
        guest.setDeviceType(env.getDeviceType());
        guest.setLocale(ps.getLocale().toString());
        guest.setTimeout(ps.getConfig().getTimeToLive());
        guest.setLoginDate(new Date());
        guest.setLastAccessTime(new Date());
        guest.setState(StateType.START);
        guest.setIdCallback(name -> ps.getIdGenerator().getNextId("sys_event_info"));
        OrgDo org = new OrgDo();
        org.setId(id);
        org.setName("guest");
        org.setExtraInfo(new HashMap<>());
        guest.setOrg(org);
        guest.setRoles(new String[]{"guest"});
        return guest;
    }
}
